
#pragma once
#include "online_user.hpp"
#include "data_manager.hpp"
#include "utils.hpp"

// 游戏房间管理

// 1. 游戏房间类 , 2. 游戏房间管理类

// 私有成员: 两个在线用户管理:大厅用户以及游戏房间用户, 数据库句柄
// 房间属性: 房间ID , 房间在线人数 , 房间两个下棋用户ID , 房间状态 : 正在比赛 , 已经结束

// 类成员函数: handle_chess , handle_chat ,handle_exit ,boardcast , handle_request

typedef enum
{
    IN_MATCH,
    GAME_OVER
} game_status;

class Room
{
#define COLS 20
#define ROWS 40
#define WHITE_PIECE 1
#define BLACK_PIECE 2

    bool palyer_win(int x, int y, int dx, int dy, int color)
    {
        int count = 1;
        int cur_x = x + dx, cur_y = y + dy;
        while (cur_x >= 0 && cur_x < COLS && cur_y >= 0 && cur_y < ROWS && _board[cur_x][cur_y] == color)
        {
            cur_x = cur_x + dx, cur_y = cur_y + dy;
            count++;
        }
        cur_x = x - dx, cur_y = y - dy;
        while (cur_x >= 0 && cur_x < COLS && cur_y >= 0 && cur_y < ROWS && _board[cur_x][cur_y] == color)
        {
            cur_x = cur_x - dx, cur_y = cur_y - dy;
            count++;
        }
        return count >= 5;
    }

public:
    Room(Online_manger *hall_user, Online_manger *room_user, User_table *user_table, uint64_t room_id)
        : _hall_user(hall_user), _room_user(room_user), _user_table(user_table), _room_id(room_id) , _status(IN_MATCH),
        _player_num(0)
    {
        Log_ns::Log(Log_ns::Debug) << "创建房间成功, 房间编号是: " << _room_id;
        _board = std::vector<std::vector<int>>(COLS, std::vector<int>(ROWS , 0));
    }

    Json::Value handle_chess(Json::Value &req)
    {
        Json::Value resp = req;

        // 1. 检查对方是否在游戏房间中
        uint64_t cur_uid = req["uid"].asUInt64();
        uint64_t other_uid = (cur_uid == _white_player_uid ? _black_player_uid : _white_player_uid);
        if (!_room_user->if_online(other_uid) || !_room_user->if_online(cur_uid))
        {
            Log_ns::Log(Log_ns::Debug) << "对方已逃跑";

            resp["result"] = true;
            resp["reason"] = "对方逃跑了";
            resp["winner"] = (_room_user->if_online(cur_uid) ? cur_uid : other_uid);
            return resp;
        }

        // 2. 检查下的位置对不对
        int col = req["row"].asInt();
        int row = req["col"].asInt();
        Log_ns::Log(Log_ns::Debug) << "用户进行下棋操作, 下棋位置: (" << col << " , " << row << ")";
        if (_board[col][row] != 0)
        {
            Log_ns::Log(Log_ns::Debug) << "无效位置";

            resp["result"] = false;
            resp["reason"] = "无效位置";
            resp["winner"] = 0;
            return resp;
        }

        // 3. 进行下棋操作
        int color = (cur_uid == _white_player_uid ? WHITE_PIECE : BLACK_PIECE);
        _board[col][row] = color;

        // 4. 检查是否可以五星连珠
        if (palyer_win(col, row, 0, 1, color) ||
            palyer_win(col, row, 1, 0, color) ||
            palyer_win(col, row, 1, 1, color) ||
            palyer_win(col, row, 1, -1, color))
        {
            Log_ns::Log(Log_ns::Debug) << "五星连珠";

            resp["reason"] = "五子连珠";
            resp["result"] = true;
            resp["winner"] = cur_uid;
            return resp;
        }

        resp["winner"] = 0;
        resp["result"] = true;
        return resp;
    }

    Json::Value handle_chat(Json::Value &req)
    {
        // 1. 检查敏感词
        // 2. 直接将信息进行返回 , 由handle_request内部进行广播
        req["result"] = true;
        return req;
    }

    void handle_exit(uint64_t uid)
    {
        // 1. 将该房间的信息进行修改
        // 2. 判断游戏状态, 如果游戏进行还在进行中, 有人退出另一方直接获胜

        _player_num--;
        _room_user->erase(uid);
        if (_status == IN_MATCH) 
        {
            Json::Value res;
            res["optype"] = "put_chess";
            res["reason"] = "对方逃跑了";
            res["result"] = true;
            res["winner"] = (uid == _white_player_uid ? _black_player_uid : _white_player_uid);
            broadcast(res);
            return ;
        }
    }

    void broadcast(Json::Value &req)
    {
        // 进行广播
        // 1. 将Json数据进行序列化 , 发送给双方
        std::string content;
        Json_util::serialization(req, content);
        websocket_server::connection_ptr white_conn_ptr = _room_user->get_conn_by_id(_white_player_uid);
        websocket_server::connection_ptr black_conn_ptr = _room_user->get_conn_by_id(_black_player_uid);

        if (white_conn_ptr.get() != nullptr)
        {
            Log_ns::Log(Log_ns::Debug) << "白方接收到消息";
            white_conn_ptr->send(content);
        }
        if (black_conn_ptr.get() != nullptr)
        {
            Log_ns::Log(Log_ns::Debug) << "黑方接收到消息";
            black_conn_ptr->send(content);
        }
    }

    Json::Value handle_request(Json::Value &req)
    {
        // 处理所有的请求, 根据请求类型进行分类
        std::string optype = req["optype"].asCString();
        Json::Value res;
        if (optype == "put_chess")
        {
            res = handle_chess(req);
            if (res["winner"].asUInt64() != 0)
            {
                _status = GAME_OVER;
                // 有胜者
                uint64_t winner = res["winner"].asUInt64();
                Log_ns::Log(Log_ns::Debug) << winner << " 获胜";

                uint64_t loser = (winner == _white_player_uid ? _black_player_uid : _white_player_uid);
                _user_table->win(winner);
                _user_table->lose(loser);
            }
        }
        else if (optype == "chat")
        {
            res = handle_chat(req);
        }
        broadcast(res);
        return res;
    }

    void Set_white_player(uint64_t uid)
    {
        Log_ns::Log(Log_ns::Debug) << uid << "加入房间: " << _room_id;
        _white_player_uid = uid;
        _player_num++;
    }

    void Set_black_player(uint64_t uid)
    {
        Log_ns::Log(Log_ns::Debug) << uid << "加入房间: " << _room_id;

        _black_player_uid = uid;
        _player_num++;
    }

    bool empty() { return _player_num == 0; }

    uint64_t Get_white_player() { return _white_player_uid; }

    uint64_t Get_black_player() { return _black_player_uid; }

private:
    Online_manger *_hall_user;
    Online_manger *_room_user;
    User_table *_user_table;

    uint64_t _room_id;
    game_status _status;
    size_t _player_num;
    uint64_t _white_player_uid;
    uint64_t _black_player_uid;

    std::vector<std::vector<int>> _board;
};

// 2. 游戏房间管理类

// 2.1 内部成员: 递增的房间编号( + 互斥锁) , 房间编号与房间的映射关系  , 玩家ID与房间的映射关系, 获取在线玩家的句柄
// 2.2 内部接口: 新增房间 , 删除房间  , 用房间ID来获取房间相关信息 , 用用户ID获取所在房间相关信息

class Room_manger
{
public:

    Room_manger(Online_manger * hall_user , Online_manger * room_user , User_table *user_table)
    :_hall_user(hall_user) , _room_user(room_user) , _user_table(user_table) , _room_num(0)
    {}
    
    
    // 1. 新增房间
    std::shared_ptr<Room> create_room(uint64_t uid1, uint64_t uid2)
    {
        // 1. 判断这两个用户是否都在游戏大厅中
        // 2. 创建房间并将两个用户加入房间
        // 3. 将房间信息保存起来
        // 4. 返回房间信息
        std::shared_ptr<Room> room_ptr;
        if (!_hall_user->if_online(uid1) || !_hall_user->if_online(uid1))
        {
            Log_ns::Log(Log_ns::Warning) << "the user is not online";
            return room_ptr;
        }

        size_t room_id;
        {
            std::unique_lock<std::mutex> lock(_mtx);
            room_id = _room_num++;
        }

        room_ptr = std::make_shared<Room>(_hall_user, _room_user, _user_table, room_id);
        room_ptr->Set_white_player(uid1);
        room_ptr->Set_black_player(uid2);

        _rid_to_rooms.emplace(room_id, room_ptr);
        _uid_to_rid.emplace(uid1, room_id);
        _uid_to_rid.emplace(uid2, room_id);

        return room_ptr;
    }

    std::shared_ptr<Room> get_room_by_uid(uint64_t uid)
    {
        if (_uid_to_rid.count(uid) == 0)
            return std::shared_ptr<Room>();
        uint64_t rid = _uid_to_rid[uid];
        std::shared_ptr<Room> room_ptr = _rid_to_rooms[rid];
        return room_ptr;
    }

    // 2. 移除房间
    void remove_room(uint64_t rid)
    {
        // 1. 获取房间中的用户, 先将用户进行移除
        // 2. 再将房间进行移除

        std::shared_ptr<Room> room_ptr = _rid_to_rooms[rid];
        uint64_t uid1 = room_ptr->Get_black_player();
        uint64_t uid2 = room_ptr->Get_white_player();

        if (_uid_to_rid.count(uid1))
            _uid_to_rid.erase(uid1);
        if (_uid_to_rid.count(uid2))
            _uid_to_rid.erase(uid2);

        _rid_to_rooms.erase(rid);
        Log_ns::Log(Log_ns::Debug) << rid << " 房间已被移除";
    }

    // 3. 移除房间中的用户
    void remove_room_user(uint64_t uid)
    {
        // 1. 获取房间信息
        // 2. 将用户从房间中进行移除
        // 3. 如果房间内用户为0, 对房间进行移除

        std::shared_ptr<Room> room_ptr = get_room_by_uid(uid);
        if(!room_ptr)
        {
            return ;
        }
        room_ptr->handle_exit(uid);
        if (room_ptr->empty())
        {
            remove_room(_uid_to_rid[uid]);
        }
        Log_ns::Log(Log_ns::Debug) << uid << " 用户已被移除";
    }

private:
    size_t _room_num;
    std::mutex _mtx;
    std::unordered_map<uint64_t, std::shared_ptr<Room>> _rid_to_rooms;
    std::unordered_map<uint64_t, uint64_t> _uid_to_rid;
    User_table *_user_table;
    Online_manger *_hall_user;
    Online_manger *_room_user;
};
